
import { ExecutiveContext, State,Executive, Autherizable, ActivityStatus, Action, ExecuteStatus, ActionType } from "../defination"
import { ActivityRecord, ByteArray, ExecutionRecord, ExecutiveRecord } from "../entity"
import {  ACTIVITY, ACTIVITY_STATUS, DEALER, EXECUTE_STATUS, EXECUTION_RECORD,  NEXT,  REQUEST,  STATE_RECORD } from "../shared-symbols"
import { BaseAction } from "./action"
import type { DoneAction } from "./doneAction"
import type { BeginAction } from "./beginAction"
import { invokeExecutiveAction } from "../executive"
import { EndAction } from "./endAction"


export const EXITED = Symbol('exited')
export class ExitAction
<
  TActivity extends Executive<TActivity,TState,TRecord,TContext>,
  TState extends State=State,
  TRecord extends ExecutiveRecord=ExecutiveRecord,
  TContext extends ExecutiveContext=ExecutiveContext
> extends BaseAction<TActivity,TState,TRecord,TContext>
{
  [EXITED]?:boolean
  constructor(act:DoneAction<TActivity,TState,TRecord,TContext>|BeginAction<TActivity,TState,TRecord,TContext>){
    super(act)
  }
  public get exited():boolean{
    return this[EXITED]===true
  }
  async invoke(context:TContext):Promise<EndAction<TActivity,TState,TRecord,TContext>|null|undefined>{
    if (this[EXITED]===true || this[EXITED]===false)
      throw new Error('exit action can only be invoked once')

    const activity = this[ACTIVITY]
    const currentStatus = activity[ACTIVITY_STATUS]
    if (currentStatus !== ActivityStatus.DONE) {
      throw new Error('only can invoke exit action when the activity is done')
    }

    // todo:
    const result = await invokeExecutiveAction<TActivity,TState,TRecord,TContext>(this.activity,ActionType.EXIT,context)
    this[EXITED] = result===false ? false : true
    if (result===false)
      return this.next
    
    const nextStatus = ActivityStatus.EXITED
    const statusBytes = context.enumConverter.toByteArray<ByteArray>(nextStatus,ActivityStatus,true)!
    const finishBytes = context.enumConverter.toByteArray<ByteArray>(ExecuteStatus.FINISHED,ExecuteStatus,true)!

    activity[EXECUTE_STATUS] = ExecuteStatus.FINISHED
    activity[ACTIVITY_STATUS] = ActivityStatus.EXITED
    const at = context.calendar.now()
    const dealerId = context[DEALER].id
    

    const activityRecord = this[STATE_RECORD] as unknown as ActivityRecord
    activity[ACTIVITY_STATUS] = nextStatus
    activityRecord.activityStatus = statusBytes
    activityRecord.exitTime = at
    activityRecord.executeStatus = finishBytes
    activityRecord.executionId = undefined!
    activityRecord.modifierId = dealerId
    activityRecord.updateTime = at

    const executionRecord = this[EXECUTION_RECORD] as ExecutionRecord
    executionRecord.activityStatus = statusBytes
    executionRecord.executeStatus = finishBytes
    executionRecord.updateTime = at
    executionRecord.overTime = at

    await context.repository.exitActivity(
      activityRecord,
      executionRecord,
      context.enumConverter.toByteArray<ByteArray>(ActivityStatus.DONE,ActivityStatus,true)!,
      context[REQUEST].requestId
    )
    return this.next!
  }

  get next():EndAction<TActivity,TState,TRecord,TContext>|undefined|null{
    if (this[EXITED]===undefined)
      return undefined
    
    if (this[NEXT]===undefined)  {
      if (this[EXITED]===true)
        return this[NEXT] = null
      else
        return this[NEXT] = new EndAction(this)
    }
  }
}